
using ContractDocumentation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;


namespace ContractDocumentation
{
    class ElementVisualizer
    {
        static XNamespace xs = "http://www.w3.org/2001/XMLSchema";

        internal static IEnumerable<FrameworkElement> VisualizeElement(Context context, XElement elementElement)
        {
            FrameworkElement mainFrameworkElement = VisualizeElementDeclaration(context, elementElement);
            if (elementElement.Element(xs + "complexType") != null)
            {
                IEnumerable<FrameworkElement> frameworkElements = ComplexTypeVisualizer.VisualizeComplexType(context, elementElement.Element(xs + "complexType"));
                return UIUtils.LayoutChildren(context, mainFrameworkElement, frameworkElements);
            }
            else
            {
                return new FrameworkElement[] { mainFrameworkElement };
            }
        }

        static FrameworkElement VisualizeElementDeclaration(Context context, XElement elementElement)
        {
            string name;
            XName refName = XmlUtils.GetXName(elementElement, (string)elementElement.Attribute("ref"));
            if (refName != null)
            {
                name = refName.LocalName;
            }
            else
            {
                name = elementElement.Attribute("name").Value;
            }
            bool isOptional = XmlUtils.GetMinOccurs(elementElement) < 1;
            bool isMulti = XmlUtils.GetMaxOccurs(elementElement) > 1;
            Grid grid = new Grid();
            Rectangle rectangle = UIUtils.CreateElementOrAttributeRectangle(isOptional);
            rectangle.Fill = Brushes.Azure;
            if (isMulti)
            {
                Rectangle rectangle2 = UIUtils.CreateElementOrAttributeRectangle(false);
                rectangle2.Fill = Brushes.Azure;
                rectangle2.Stroke = Brushes.Gray;

                Rectangle rectangle3 = UIUtils.CreateElementOrAttributeRectangle(false);
                rectangle3.Fill = Brushes.Azure;
                rectangle3.Stroke = Brushes.Gray;

                rectangle.Margin = new Thickness(0, 0, 8, 8);
                rectangle2.Margin = new Thickness(4, 4, 4, 4);
                rectangle3.Margin = new Thickness(8, 8, 0, 0);

                grid.Children.Add(rectangle3);
                grid.Children.Add(rectangle2);
            }
            grid.Children.Add(rectangle);
            TextBlock textBlock = new TextBlock();
            textBlock.Style = (Style)context.ResourceDictionary["ElementTextBlock"];
            grid.Children.Add(textBlock);

            if (isMulti)
            {
                textBlock.Padding = new Thickness(4, 4, 12, 12);
                grid.Tag = new VisualizerTag()
                {
                    GetLeftConnectionPoint = () => new Point(0, (grid.DesiredSize.Height / 2) - 4),
                    GetRightConnectionPoint = () => new Point(grid.DesiredSize.Width - 8, (grid.DesiredSize.Height / 2) - 4),
                };
            }
            Bold elementNameBold = new Bold();
            elementNameBold.Inlines.Add(name);
            textBlock.Inlines.Add(elementNameBold);
            if (elementElement.Attribute("type") != null)
            {
                XName type = XmlUtils.GetXName(elementElement, (string)elementElement.Attribute("type"));
                textBlock.Inlines.Add(new LineBreak());
                textBlock.Inlines.Add(type.LocalName);
                var subTypes = XmlUtils.GetDerivedComplexTypes(context, type).Where(t => !XmlUtils.IsAbstract(context, t)).Select(n => n.LocalName);
                if (subTypes.Count() > 0)
                {
                    textBlock.Inlines.Add(new LineBreak());
                    Run run = new Run()
                    {
                        Text = "Derived types:\n" + string.Join("\n", subTypes.ToArray()),
                        Foreground = Brushes.Gray,
                    };
                    textBlock.Inlines.Add(run);
                }
            }
            var q = from simpleTypeElement in elementElement.Elements(xs + "simpleType")
                    from restrictionElement in simpleTypeElement.Elements(xs + "restriction")
                    select restrictionElement;
            foreach (XElement restrictionElement in q)
            {
                string type = XmlUtils.GetXName(restrictionElement, (string)restrictionElement.Attribute("base")).LocalName;
                textBlock.Inlines.Add(new LineBreak());
                textBlock.Inlines.Add("restriction of " + type);
            }

            grid.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
            if (refName != null)
            {
                grid.Tag = new VisualizerTag()
                {
                    Links = new SchemaLink[] {
                        new SchemaLink()
                        {
                            Rect = isMulti ? new Rect(new Size(grid.DesiredSize.Width - 8, grid.DesiredSize.Height - 8)) : new Rect(grid.DesiredSize),
                            Id = "element_" + refName.LocalName,
                        }
                    },
                };
            }
            string doc = XmlUtils.GetXsDocumentation(elementElement);
            return UIUtils.AppendDocumentation(context, grid, doc, Math.Max(grid.DesiredSize.Width, 192));
        }
    }
}
